#ifdef __cplusplus
#include <cstdlib>
#else
#include <stdlib.h>
#endif
#include "SDL_image.h"
#include <string>
#include "SDL.h"
#include <fstream>
#include <time.h>

// O Block Structures
#define OP1 {{ 0, 0, 0, 0},{ 0, 1, 1, 0},{ 0, 1, 1, 0}, { 0, 0, 0, 0}}
// Line Block Structures
#define LineP1 {{ 0, 0, 2, 0},{ 0, 0, 2, 0},{ 0, 0, 2, 0}, { 0, 0, 2, 0}}
// S Block Structures
#define SP1 {{0,0,0,0},{0,0,3,3},{0,3,3,0},{0,0,0,0}}
// Z Block Structures
#define ZP1 {{0,0,0,0},{0,4,4,0},{0,0,4,4},{0,0,0,0}}
// L Block Structures
#define LP1 {{0,0,0,0},{0,5,5,5},{0,5,0,0},{0,0,0,0}}
// J Block Structure
#define JP1 {{0,0,0,0},{0,6,6,6},{0,0,0,6},{0,0,0,0}}
// T Block Structure
#define TP1 {{0,0,7,0},{0,7,7,7},{0,0,0,0},{0,0,0,0}}
const unsigned short int SCREEN_WIDTH = 800;
const unsigned short int SCREEN_HEIGHT = 640;
const unsigned short int SCREEN_BPP = 32;

// Struct Block represents the block moving down the playing field
struct Block
{
    int xPosition;
    int yPosition;
    unsigned short int piece[4][4];
};

// Struct Field represents the game playing field
struct Field
{
    unsigned short int board[23][12];
};

// checkCollision
// Parameters: two block type objects and two offsets for positioning
// Return: Tells if based on the currentBlock being at the offsets on the field if it would overlap
bool checkCollision( const Block &currentBlock, const Field &field, int xOffset, int yOffset );
Block rotateBlock( const Block &gameBlock, const Field &field );
Block generateGameBlock( );
int generateRandomNumber( int min, int max );
int main ( int argc, char** argv )
{
    // Initialize the SDL Video Interface
    if ( SDL_Init( SDL_INIT_VIDEO ) < 0 )
    {
        printf( "Unable to init SDL: %s\n", SDL_GetError() );
        return 1;
    }
    char tmp;
    int pieceType;
    int speed = 750;
    Block gameBlock = generateGameBlock( );
    Block temporarygameBlock;
    // Open a Sample level
    std::ifstream sampleField ( "tetrisFieldTest.txt" , std::ifstream::in );
    Field field;

    int cycleTimer = SDL_GetTicks( );

    // Load the Level ( Put in a function later possibly )
    for ( int i = 0; i < 23; i++ )
    {
        for ( int j = 0; j < 12; j++ )
        {
            tmp = sampleField.get( );
           field.board[i][j] = atoi( &tmp );
        }
        sampleField.get( );
    }

    // Set SDL to quit properly on exit
    atexit(SDL_Quit);

    // Creates the game window
    SDL_Surface* screen = SDL_SetVideoMode( SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP,
                                           SDL_HWSURFACE|SDL_DOUBLEBUF);
    if ( !screen )
    {
        printf("Unable to set 640x480 video: %s\n", SDL_GetError());
        return 1;
    }

    // load an image
    SDL_Surface* tiles = IMG_Load("tetrisTiles3.png");
    SDL_Surface* background = IMG_Load("background.png");
    SDL_Rect backgroundRect = {0, 0, 320, 640};
    if (!tiles)
    {
        printf("Unable to load bitmap: %s\n", SDL_GetError());
        return 1;
    }

    // Create the block color types
    SDL_Rect blocks[8];
    blocks[0] = { 0, 0, 0, 0 };
    blocks[1] = { 0, 0, 32, 32};
    blocks[2]= { 32, 0, 32, 32};
    blocks[3] = { 64, 0, 32, 32 };
    blocks[4] = { 96, 0, 32, 32 };
    blocks[5] = { 128, 0, 32, 32 };
    blocks[6] = { 160, 0, 32, 32 };
    blocks[7] = { 192, 0, 32, 32 };

    // The Program's main loop
    bool done = false;
    bool shift = true;
    bool clearRow = false;
    while (!done)
    {
        // message processing loop
        SDL_Event event;
        while (SDL_PollEvent(&event))
        {
            // check for messages
            switch (event.type)
            {
                // exit if the window is closed
            case SDL_QUIT:
                done = true;
                break;

                // check for keypresses
            case SDL_KEYDOWN:
                {
                    // exit if ESCAPE is pressed
                    if (event.key.keysym.sym == SDLK_ESCAPE)
                        done = true;
                    else if ( event.key.keysym.sym == SDLK_LEFT )
                    {
                        if ( checkCollision( gameBlock, field, gameBlock.xPosition - 1, gameBlock.yPosition ) == false )
                            gameBlock.xPosition -= 1;
                    }
                    else if ( event.key.keysym.sym == SDLK_RIGHT )
                    {
                        if ( checkCollision( gameBlock, field, gameBlock.xPosition + 1, gameBlock.yPosition ) == false )
                            gameBlock.xPosition += 1;
                    }
                    else if ( event.key.keysym.sym == SDLK_UP )
                    {
                        gameBlock = rotateBlock( gameBlock, field );
                    }
                    else if ( event.key.keysym.sym == SDLK_DOWN )
                    {
                        if ( checkCollision( gameBlock, field, gameBlock.xPosition, gameBlock.yPosition + 1 ) == false )
                            gameBlock.yPosition += 1;
                    }
                    break;
                }
            } // end switch
        } // end of message processing

        if ( SDL_GetTicks( ) - cycleTimer > speed )
        {
            cycleTimer = SDL_GetTicks( );
            if ( speed > 250 ) speed = int(speed * 0.99);
            if ( checkCollision( gameBlock, field, gameBlock.xPosition, gameBlock.yPosition + 1 ) == false )
                gameBlock.yPosition += 1;
            else
            {
                // Merge the piece into the playing field
                for ( int y = gameBlock.yPosition; y < (gameBlock.yPosition + 4); y++ )
                {

                    for ( int x = gameBlock.xPosition; x < (gameBlock.xPosition + 4); x++ )
                    {
                        if ( field.board[y][x] == 0 && gameBlock.piece[y - gameBlock.yPosition][x - gameBlock.xPosition] != 0 )
                            field.board[y][x] = gameBlock.piece[y - gameBlock.yPosition ][x - gameBlock.xPosition ];
                    }
                }
                for ( int y = gameBlock.yPosition; y < gameBlock.yPosition + 4; y++ )
                {
                    clearRow = true;
                    for ( int x = 1; x < 11; x++ )
                    {
                        if ( field.board[y][x] == 0 )
                        {
                            clearRow = false;
                        }
                    }
                    if ( clearRow == true )
                    {
                        for ( int x = 1; x < 11; x++ )
                        {
                            field.board[y][x] = 0;
                        }
                        for ( int i = y; i > 0; i-- )
                        {
                            for ( int x = 1; x < 11; x++ )
                            {
                                field.board[i][x] = field.board[i-1][x];
                            }
                        }
                    }
                }
                gameBlock = generateGameBlock( );
            }
        }

        // DRAWING STARTS HERE

        // clear screen
        SDL_FillRect(screen, 0, SDL_MapRGB(screen->format, 0xf4, 0x9a, 0xc2));
        SDL_BlitSurface( background, &backgroundRect, screen, 0 );
        for ( int y = 2; y < 22; y++ )
        {
            for ( int x = 1; x < 11; x++ )
            {
                if ( field.board[y][x] > 0 )
                {
                    SDL_Rect tmpRect = {(x-1)*32,(y-2)*32,32,32};
                    SDL_BlitSurface(tiles, &blocks[field.board[y][x]], screen, &tmpRect );
                }
            }
        }
        for ( int y = 0; y < 4; y++ )
        {
            for ( int x = 0; x < 4; x++ )
            {
                if ( gameBlock.piece[y][x] > 0 )
                {
                    SDL_Rect tmpRect = {(x-1+gameBlock.xPosition)*32,((y-2)+gameBlock.yPosition)*32, 32, 32 };
                    SDL_BlitSurface( tiles, &blocks[gameBlock.piece[y][x]], screen, &tmpRect );
                }
            }
        }
        // finally, update the screen :)
        SDL_Flip(screen);
    } // end main loop

    // free loaded bitmap
    SDL_FreeSurface(tiles);
    sampleField.close( );
    // all is well ;)
    return 0;
}


bool checkCollision( const Block &currentBlock, const Field &field, int xOffset, int yOffset )
{
    for ( int y = 0; y < 4; y++ )
    {
        for ( int x = 0; x < 4; x++ )
        {
            if (( currentBlock.piece[y][x] ) && ( field.board[y + yOffset][x + xOffset] ) )
                return true;
        }
    }
    return false;
}

Block rotateBlock( const Block &gameBlock, const Field &field )
{
    Block resultBlock;
    for ( int y = 0; y < 4; y++ )
    {
        for ( int x = 0; x < 4; x++ )
        {
            resultBlock.piece[y][x]=gameBlock.piece[3-x][y];
        }
    }
    resultBlock.xPosition = gameBlock.xPosition;
    resultBlock.yPosition = gameBlock.yPosition;
    if ( checkCollision( resultBlock, field, resultBlock.xPosition, resultBlock.yPosition ) )
        return gameBlock;
    else
        return resultBlock;
}

Block generateGameBlock( )
{
    Block newBlock;
    int pieceType = 0;
    srand( SDL_GetTicks( ) );
    pieceType = generateRandomNumber( 0, 6 );
    if ( pieceType == 0 )
        newBlock.piece = OP1;
    else if ( pieceType == 1 )
        newBlock.piece = LineP1;
    else if ( pieceType == 2 )
        newBlock.piece = SP1;
    else if ( pieceType == 3 )
        newBlock.piece = ZP1;
    else if ( pieceType == 4 )
        newBlock.piece = LP1;
    else if ( pieceType == 5 )
        newBlock.piece = JP1;
    else if ( pieceType == 6 )
        newBlock.piece = TP1;
    newBlock.xPosition = 5;
    newBlock.yPosition = 3;
    return newBlock;
}

int generateRandomNumber( int min, int max )
{
    srand( SDL_GetTicks( ) );
    return ( rand( ) % ( (max + 1) - min) ) + min;
}
